package dreic;

import java.io.PrintWriter;


/**
 * This class implements a pretty printer for abstract syntax
 * trees in Drei
 */
class Printer(out: PrintWriter) {
  //import Operators._;

  /**
   * Indentation string.
   */
  private val step = "  ";

  /**
   * indentation level.
   */
  private var level = 0;

  /**
   * Prints a tree node.
   */
  def print(tree: Tree): Printer = tree match {
    case Program(decls, main) =>
      for (val d <- decls)  print(d).println;
      print(main).println
    
    case ClassDef(name , Some(name2), members) =>
        print("class ").print(name).print(" extends ").print(name2).print(" {").indent.println.print(members).undent.println.print("}")

    case ClassDef(name , None, members) =>
         print("class ").print(name).print(" {").indent.println.print(members).undent.println.print("}")

    case MethodDef(name, args, Some(type_methode), corps) =>
         print("def ").print(name).print("(").printFormals(args).print(") : ").print(type_methode).print(" = ").indent.println.print(corps).print(";").undent.println;
    case MethodDef(name, args, None, corps) =>
         print("def ").print(name).print("(").printFormals(args).print(") ").indent.println.print(corps).undent.println;
    case FieldDecl(name , field_type) =>
        print("val ").print(name).print(" : ").print(field_type).print(";")
    case Compound(stats) =>
	print("{").indent.println.print(stats).undent.println.print("}")
    case Block(stats, Some(return_value)) =>
         print("{").indent.println.print(stats).println.print("return ").print(return_value).undent.println.print("} ")
    case Block(stats, None) =>
         print("{").indent.println.print(stats).undent.println.print("} ")
    case Call(expr , name, exprs) => print(expr).print(".").print(name).print("(").printParams(exprs).print(")")
    case Ident(n) => print(n)
    case PrintChar(n) => print("printChar(").print(n).print(")").print(";")
    case ReadChar() => print("readChar");
    case PrintInt(n) => print("printInt(").print(n).print(")").print(";");
    case ReadInt() => print("readInt");
    case ClassType(n) => print(n);
    case IntType() => print("Int");
    case NullType() => print("Null");
//    case StringLit(value) => print("\"").print(value).print("\"");
    case IntLit(i) => print(String.valueOf(i));
    case NullLit() => print("null");
    case While(cond , stats) => print("while").print("( ").print(cond).print(" )").print("{").indent.println.print(stats).println.undent.print("}")
    case Binop(op, left, right) => print("( ").print(left).print(" ").print(op.toString()).print(" ").print(right).print(" )")
    case Unop(op, expr) => print(op.toString()).print("(").print(expr).print(")")
    case Set(name , expr) => print("set ").print(name).print(" = ").print(expr).print(";")
    case Var(varname, vartype, init) => print("var ").print(varname).print(" : ").print(vartype).print(" = ").print(init).print(";")
    case New(name, exprs) => print("new ").print(name).print("(").printParams(exprs).print(")")
    case Do(expr) => print("do ").print(expr).print(";")
    case If(cond, iftrue, iffalse) => print("if ").print("(").print(cond).print(") ").indent.println.print(iftrue).undent.println.print(" else ").indent.println.print(iffalse).undent.println
    case Select(prefix, selector) =>
      print(prefix).print(".")//.print(selector)  TODO: resolve this somehow.
  }

  
  /**
   * Print abstract syntax trees separated by commas.
   */
  private def print(trees: List[Tree]): Printer = trees match {
    case x::Nil => print(x)
    case x::xs => print(x).println.print(xs)
    case _ => this
  }
  
/**
* Printing method params
*/
  private def printParams(trees: List[Tree]): Printer = trees match {
    // ... a completer ...
    case x::Nil => print(x)
    case x::xs => print(x).print(", ").printParams(xs)
    case _ => this
  }

  /**
   * Print a string.
   */
  private def print(value: String): Printer = {
    out.print(value);
    this
  }
  
  /**
  * Print optionnal stuff
  */
  private def print(opt: Option[Expr]): Printer = opt match{
    case Some(a) => print(a);
    case None => this;
  }
  private def print(name: Name): Printer = print(name.name);

  private def print(formal: Pair[Name, TypeTree]): Printer =
    print(formal._1).print(" : ").print(formal._2);

  /**
   * Print abstract syntax trees separated by commas.
   */
  private def printFormals(formals: List[Pair[Name, TypeTree]]): Printer =
    formals match {
//      case // ... a completer ...
      case x::Nil => print(x)
      case Pair(n, t)::xs => print(n).print(" : ").print(t).print(", ").printFormals(xs)
      case _ => this
    }

  /**
   * Print an end-of-line character and indent the following
   * line appropriately.
   */
  private def println = {
    out.println();
    for (val i <- List.range(0, level)) out.print(step);
    this
  }

  /**
   * Increment the indentation level.
   */
  private def indent = {
    level = level + 1;
    this
  }

  /**
   * Decrement the indentation level.
   */
  private def undent = {
    level = level - 1;
    this
  }
}
